package com.coder.rental.controller;

import cn.hutool.core.convert.NumberWithFormat;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWTPayload;
import com.coder.rental.entity.Permission;
import com.coder.rental.entity.User;
import com.coder.rental.security.CustomerAuthenticationException;
import com.coder.rental.service.IUserService;
import com.coder.rental.utils.JwtUtils;
import com.coder.rental.utils.RedisUtils;
import com.coder.rental.utils.Result;
import com.coder.rental.utils.RouteTreeUtils;
import com.coder.rental.vo.RouteVO;
import com.coder.rental.vo.TokenVo;
import com.coder.rental.vo.UserInfoVo;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@RestController
@RequestMapping("/rental/auth/")
public class AuthController {
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private IUserService userService;

    /**
     * 刷新token的接口
     * 当用户接近token过期时间但仍在活跃时调用此接口以延长token的有效期
     *
     * @param request HTTP请求对象，用于获取当前请求的token
     * @return 返回Result对象，包含刷新后的token信息
     */
    @PostMapping("/refreshToken")
    public Result refreshToken(HttpServletRequest request){
        //获取token
        String token = request.getHeader("token");
        if (StrUtil.isEmpty(token)){
            token = request.getParameter("token");
        }
        //获取当前认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //获取当前用户信息
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        //解析token以获取用户名
        String username = JwtUtils.parseToken(token).getClaim("username").toString();
        String newToken = "";
        //验证token中的用户名与当前认证的用户名是否一致
        if (StrUtil.equals(username,userDetails.getUsername())){
            //准备用户信息以生成新的token
            Map<String,Object> map = new HashMap<>();
            map.put("username",userDetails.getUsername());
            // 调用JwtUtils.createToken方法，根据map中的用户信息创建一个JWT令牌
            newToken = JwtUtils.createToken(map);
        }else {
            throw new CustomerAuthenticationException("token数据异常");
        }
        //获取本次token过期时间
        // 解析JWT令牌中的特定声明，并将其转换为指定的类型
        // 这里获取的是令牌的过期时间声明
        NumberWithFormat claim = (NumberWithFormat) JwtUtils.parseToken(newToken).getClaim(JWTPayload.EXPIRES_AT);

        // 将获取到的声明转换为DateTime类型，以便进行时间相关的操作
        // 这一步是必要的，因为JWT令牌中的过期时间是以Unix时间戳形式存储的
        DateTime dateTime = (DateTime) claim.convert(DateTime.class, claim);

        // 获取转换后的时间的毫秒值，用于后续的比较或计算
        // 这样做是为了方便在程序中统一处理时间，确保时间比较或计算的准确性
        long expireTime = dateTime.getTime();
        //创建TokenVo对象以存储新的token和过期时间
        TokenVo tokenVo = new TokenVo();
        tokenVo.setToken(newToken).setExpireTime(expireTime);
        //清除原有token
        redisUtils.del("token:"+token);
        //计算当前时间到token过期时间的秒数
        long nowTime = DateTime.now().getTime();
        String newTokenKey = "token:"+newToken;
        //在Redis中存储新的token，有效期为从当前时间到新token过期时间
        redisUtils.set(newTokenKey,newToken,(expireTime-nowTime)/1000);
        //返回刷新后的token信息
        return Result.success(tokenVo).setMessage("刷新token成功");
    }

    /**
     * 获取当前认证用户的信息
     *
     * 此方法通过Spring Security框架获取当前认证用户的信息，并构建一个包含用户基本信息和权限的响应对象
     * 如果当前认证信息为空，则返回失败的响应
     *
     * @return Result对象，包含用户信息或错误信息
     */
    @GetMapping("/info")
    public Result getUserInfo(){
        // 从securityContextHolder上下文中获取认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        // 检查认证信息是否为空
        if (authentication == null){
            // 如果认证信息为空，返回失败的响应
            return Result.fail().setMessage("认证信息为空");
        }

        // 获取用户信息
        User user = (User) authentication.getPrincipal();
        // 获取用户角色名称
        List<String> list = userService.selectRoleName(user.getId());
        Object[] array = list.toArray();//将角色名称列表转换为数组
        // 构建用户信息响应对象
        UserInfoVo userInfoVo = new UserInfoVo(user.getId(),user.getUsername(),
                user.getAvatar(),user.getNickname(),array);
        // 返回成功获取用户信息的响应
        return Result.success(userInfoVo).setMessage("获取用户信息成功");
    }

    @GetMapping("/menuList")
    public Result getMenuList(){
        //获取当前用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        // 检查认证信息是否为空
        if (authentication == null){
            // 如果认证信息为空，返回失败的响应
            return Result.fail().setMessage("认证信息为空");
        }
        User user = (User) authentication.getPrincipal();
        //获取用户的权限列表
        List<Permission> permissionList = user.getPermissionList();
        //获取用户的菜单
        //当permission_type为2的按钮移除，不需要生成对应的菜单
        permissionList.removeIf(permission -> Objects.equals(permission.getPermissionType(),2));
        List<RouteVO> routeVOList = RouteTreeUtils.buildRouteTree(permissionList, 0);
        return Result.success(routeVOList).setMessage("获取菜单列表成功");
    }

}
